﻿/*
 * LinearRenderer
 * 
 * initial implementation : 20.10.2008
 *
 * Copyright (c) Adam Sida, asida@centrum.cz.
 * All rights reserved.
 * 
 * for license see Petra.Gis.License.txt
 */
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Drawing;
using Petra.Gis.Geometry;
using Petra.Gis.Geometry.Support;
using Petra.Gis.Geometry.Utils;
using Petra.Gis.DataServices;
using Petra.Gis.RenderingServices.PlanarBase;
using Petra.Gis.ReferenceSystemServices.Transformations;


namespace Petra.Gis.RenderingServices.GDIPlus
{

  public partial class LinearRenderer : EdgeGeomRenderer
  {

    //important !!! this is array of output points for rendered geometries
    //should work as heap
    //GC should leave this variable untouched (same size) during procedure calls (and resize)
    //initial size is 100 000 pts, should be enough for typical geometry


    //public LinearRenderer(BitmapMap map, RenderStyle s)
    //  :base(map, s){ }





    public override void PrepareToRender()
    {
      base.PrepareToRender();
      _outpen = new Pen(Color.FromArgb((int)renderStyle["Color"]), (int)renderStyle["LineWidth"]);

    }





    public override DomainGeometryTypeEnum RendererGeometryDomain
    {
      get { return DomainGeometryTypeEnum.Line; }
    }






    //todo:update...
    public override bool IsRenderable(GeometryBase geometry)
    {
      return true ;
    }






    public override void RenderGeometry(GeometryBase geometry)
    {
      // do transform first and then render
      renderGeometry(_forwardTransform.Transform(geometry));
    }






    protected void renderGeometry(GeometryBase geometry)
    {
      if (geometry is LineString)
        renderPolyline((LineString)geometry);
      else if (geometry is Petra.Gis.Geometry.Path)
        renderPath((Petra.Gis.Geometry.Path)geometry);
      else if (geometry is Arc)
        renderArc((Arc)geometry);
      else if (geometry is MultiLine)
        foreach (GeometryBase g in (MultiLine)geometry)
          renderGeometry(g);
      else if (geometry is GeometryCollection)
        foreach (GeometryBase g in (GeometryCollection)geometry)
          renderGeometry(g);
    }


   

    //draws polyline
    private void renderPolyline(LineString geom)
    {

      //_PointIndex = 0;
      resetPointArray();

      
      //if something is ready to output...
      if (geometryToPoints(geom.coordList.getBuffer(), geom.coordList.position, geom.coordList.Count, geom.coordList.dimension))
      {
        trimPointArrayForCurve();

        //add points to path
        _outGraphic.DrawLines(_outpen, outPts);
      }
      

    }




    //draws composite curve
    private void renderPath(Petra.Gis.Geometry.Path geom)
    {

      bool hasOutp = false;
      //bool canDraw = false;


      //_PointIndex = 0;
      resetPointArray();

      foreach (GeometryBase g in geom)
      { 
        if(g is LineString)
          hasOutp |= geometryToPoints(((LineString)g).coordList.getBuffer(), ((LineString)g).coordList.position, ((LineString)g).coordList.Count, ((LineString)g).coordList.dimension);
        else if (g is Arc)
        {
          //stroke arc first
          LineString l = ArcMath.getStrokedArc((Arc)g);
          hasOutp |= geometryToPoints(l.coordList.getBuffer(), l.coordList.position, l.coordList.Count, l.coordList.dimension);
        }

      }

      //if something is ready to output...
      if (hasOutp)
      {
        trimPointArrayForCurve();

        //add points to path
        _outGraphic.DrawLines(_outpen, outPts);
      }



    }




    //arc
    private void renderArc(Arc geom)
    {

      //_PointIndex = 0;
      resetPointArray();

      //if arc diameter is larger then precision of float, stroke (flatten) arc by Petra.Gis
      //double d = unchecked((float)(geom.radius * 2));
      //if(d != (geom.radius * 2))
      //{
        LineString l = ArcMath.getStrokedArc(geom); //currently stroke only, maybe implement arc clipping here
        
      //}

      //stroke (flatten) by GDI+, transform by Petra.Gis
      //PointF[] pts = gdiPlusStrokeArc(geom);

        

      //if something is ready to output...
      if (geometryToPoints(l.coordList.getBuffer(), l.coordList.position, l.coordList.Count, l.coordList.dimension))
      {
        trimPointArrayForCurve();

        //add points to path
        _outGraphic.DrawLines(_outpen, outPts);
      }


    }



    //multiline
    private void drawMultiLine(MultiLine geom)
    {
      foreach (GeometryBase g in geom)
      {
        _PointIndex = 0;

        renderGeometry(g);

      }
    }




    //geometrycollection
    private void drawMultiLine(GeometryCollection geom)
    {
      foreach (GeometryBase g in geom)
      {
        //_PointIndex = 0;

        renderGeometry(g);

      }
    }





    protected void trimPointArrayForCurve()
    {

      // ok something is ready to output...
      // if less than 2 points must add some extra...
      // geometry was to small for this scale or close to edge,
      // output colapses to only dot
      // put 1 extra point...
      if (_PointIndex == 1)
        addPointToArray(outPts[0].X + 1, outPts[0].Y);

      if (outPts.Length > _PointIndex)
        Array.Resize<System.Drawing.Point>(ref outPts, _PointIndex);

    }



  }
}
